package easycloud.ecs.provider.baidu;

import java.io.File;
import java.util.List;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.baidu.inf.iis.bcs.BaiduBCS;
import com.baidu.inf.iis.bcs.auth.BCSCredentials;
import com.baidu.inf.iis.bcs.model.DownloadObject;
import com.baidu.inf.iis.bcs.model.ObjectListing;
import com.baidu.inf.iis.bcs.model.ObjectMetadata;
import com.baidu.inf.iis.bcs.policy.Policy;
import com.baidu.inf.iis.bcs.request.GetObjectRequest;
import com.baidu.inf.iis.bcs.request.ListObjectRequest;
import com.baidu.inf.iis.bcs.request.PutObjectRequest;
import com.baidu.inf.iis.bcs.response.BaiduBCSResponse;

import easycloud.ecs.api.IProvider;
import easycloud.ecs.api.IPutResult;
import easycloud.ecs.api.ProviderException;
import easycloud.ecs.api.model.IStorageObject;
import easycloud.ecs.api.model.IStorageObjectListing;
import easycloud.ecs.api.model.IStorageObjectMetadata;
import easycloud.ecs.config.ConfigException;
import easycloud.ecs.config.ProviderConfig;
import easycloud.ecs.config.ProviderConfigUtils;
import easycloud.ecs.config.ProviderStatistics;
import easycloud.ecs.provider.baidu.model.BaiduStorageObject;
import easycloud.ecs.provider.baidu.model.BaiduStorageObjectListing;
import easycloud.ecs.provider.baidu.model.BaiduStorageObjectMetadata;

public class BaiduBCSProvider implements IProvider {

	private ProviderConfig config;

	private ProviderStatistics statistics;

	private static BaiduBCS adapter;

	private boolean isReady = false;

	private static final Log logger = LogFactory.getLog(BaiduBCSProvider.class);

	private static String BUCKET_NAME;

	private static final int RETRY_TIMES = 3;

	public BaiduBCSProvider(ProviderConfig config) {
		this.config = config;
		statistics = new ProviderStatistics();
		BaiduBCSProvider.adapter = new BaiduBCS(new BCSCredentials(
				this.config.getAccessKey(), this.config.getSecretKey()),
				"bcs.duapp.com");
		BaiduBCSProvider.adapter.setDefaultEncoding("UTF-8");
		BaiduBCSProvider.BUCKET_NAME = this.config.getBucketName();
	}

	/**
	 * 
	 */
	@Override
	public boolean delete(String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		boolean result = false;
		try {
			statistics.incNumberOfDelete();
			BaiduBCSProvider.adapter.deleteObject(BaiduBCSProvider.BUCKET_NAME,
					key);
			result = true;
			BaiduBCSProvider.logger.info(getName() + " delete OK: " + key);
		} catch (Exception e) {
			BaiduBCSProvider.logger.warn(getName() + " delete failed", e);
			throw new ProviderException(getName() + " delete failed", e, key);
		}
		return result;
	}

	/**
	 * 
	 * @param key
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObject getFile(String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		DownloadObject result = null;
		try {
			statistics.incNumberOfGet();
			GetObjectRequest getObjectRequest = new GetObjectRequest(
					BaiduBCSProvider.BUCKET_NAME, key);
			BaiduBCSResponse<DownloadObject> response = BaiduBCSProvider.adapter
					.getObject(getObjectRequest);
			result = response.getResult();
			BaiduBCSProvider.logger.info(getName() + " getFile OK: " + key);
		} catch (Exception e) {
			BaiduBCSProvider.logger.warn(getName() + " getFile failed", e);
			throw new ProviderException(getName() + " getFile failed", e, key);
		}
		if (result != null) {
			return new BaiduStorageObject(result);
		} else {
			return null;
		}
	}

	@Override
	public String getName() {
		return "BaiduBCS";
	}

	@Override
	public ProviderStatistics getProviderStatistics() {
		return statistics;
	}

	/**
	 * 
	 * @param key
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IStorageObjectMetadata head(String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		ObjectMetadata result = null;
		try {
			statistics.incNumberOfHead();
			BaiduBCSResponse<ObjectMetadata> response = BaiduBCSProvider.adapter
					.getObjectMetadata(BaiduBCSProvider.BUCKET_NAME, key);
			result = response.getResult();
			BaiduBCSProvider.logger.info(getName() + " head OK: " + key);
		} catch (Exception e) {
			BaiduBCSProvider.logger.warn(getName() + " head failed", e);
			throw new ProviderException(getName() + " head failed", e, key);
		}
		if (result != null) {
			return new BaiduStorageObjectMetadata(result);
		} else {
			return null;
		}
	}

	/**
	 * 
	 */
	@Override
	public IStorageObjectListing list(String path, String delimiter)
			throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		ObjectListing result = null;
		try {
			ListObjectRequest request = new ListObjectRequest(
					BaiduBCSProvider.BUCKET_NAME);
			request.setPrefix("/" + path);
			if (delimiter != null) {
				request.setListModel(2);
			} else {
				request.setListModel(1);
			}
			statistics.incNumberOfList();
			BaiduBCSResponse<ObjectListing> response = BaiduBCSProvider.adapter
					.listObject(request);
			result = response.getResult();
			BaiduBCSProvider.logger.info(getName() + " list OK: " + path);
		} catch (Exception e) {
			BaiduBCSProvider.logger.warn(getName() + " list failed", e);
			throw new ProviderException(getName() + " list failed", e, path);
		}
		if (result != null) {
			return new BaiduStorageObjectListing(result);
		} else {
			return null;
		}
	}

	@Override
	public List<String> listRoots() throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		List<String> result = null;
		try {
			ListObjectRequest request = new ListObjectRequest(
					BaiduBCSProvider.BUCKET_NAME);
			request.setPrefix("/");
			request.setListModel(1);
			statistics.incNumberOfListRoots();
			BaiduBCSResponse<ObjectListing> objectListing = BaiduBCSProvider.adapter
					.listObject(request);
			if (objectListing.getResult() != null) {
				// objectListing.getResult().getObjectSummaries();
				// TODO
				result = null;
			}
			BaiduBCSProvider.logger.info(getName() + " listRoots OK");
		} catch (Exception e) {
			BaiduBCSProvider.logger.warn(getName() + " listRoots failed", e);
			throw new ProviderException(getName() + " listRoots failed", e, "");
		}
		return result;
	}

	/**
	 * 
	 * @param file
	 * @param key
	 * @return
	 * @throws ProviderException
	 */
	@Override
	public IPutResult putFile(File file, String key) throws ProviderException {
		if (!ready()) {
			throw new ProviderException(getName() + " Provider is not ready");
		}

		ObjectMetadata putResult = null;
		if (file.isFile() && (file.length() > 0)) {
			try {
				PutObjectRequest request = new PutObjectRequest(
						BaiduBCSProvider.BUCKET_NAME, key, file);
				ObjectMetadata metadata = new ObjectMetadata();
				request.setMetadata(metadata);
				statistics.incNumberOfPut();
				putResult = BaiduBCSProvider.adapter.putObject(request)
						.getResult();
				BaiduBCSProvider.logger.info(getName() + " putFile OK: " + key);
			} catch (Exception e) {
				BaiduBCSProvider.logger.warn(getName() + " putFile failed", e);
				throw new ProviderException(getName() + " putFile failed", e,
						key);
			}
		}
		return new BaiduPutResult(putResult);
	}

	/**
	 * 
	 */
	@Override
	public boolean ready() {
		if (isReady) {
			return true;
		}

		boolean result = false;
		try {
			if (StringUtils.isEmpty(BaiduBCSProvider.BUCKET_NAME)) {
				BaiduBCSProvider.BUCKET_NAME = UUID.randomUUID().toString();
				config.setBucketName(BaiduBCSProvider.BUCKET_NAME);
				BaiduBCSProvider.adapter
						.createBucket(BaiduBCSProvider.BUCKET_NAME);
				ProviderConfigUtils.saveProviderConifg(config);
			}

			int retry = 0;
			while (!result && (retry++ < BaiduBCSProvider.RETRY_TIMES)) {
				try {
					BaiduBCSResponse<Policy> response = BaiduBCSProvider.adapter
							.getBucketPolicy(BaiduBCSProvider.BUCKET_NAME);
					result = response.getResult() != null;
					if (result) {
						isReady = true;
						break;
					}
				} catch (Exception e) {
					BaiduBCSProvider.logger.warn(getName()
							+ " query bucket failed", e);
				}
				BaiduBCSProvider.BUCKET_NAME = UUID.randomUUID().toString();
				config.setBucketName(BaiduBCSProvider.BUCKET_NAME);
				BaiduBCSProvider.adapter
						.createBucket(BaiduBCSProvider.BUCKET_NAME);
				ProviderConfigUtils.saveProviderConifg(config);
			}
		} catch (ConfigException e) {
			BaiduBCSProvider.logger.warn(getName() + " init failed", e);
			result = false;
		}
		return result;
	}

	@Override
	public void setProviderStatistics(ProviderStatistics stat) {
		statistics = stat;
	}

}
